﻿//  排除rand等函数所在的单元格
//  ExternName也会被排除，相当于跨book引用
//  重算后另存再读取比较
#if ENABLE_ET_COM
#define ENABLE_AutoItX

#if DEBUG
#define CC_DUMP_FILE
#endif

namespace suite_compare_cell
{
    using System;
    using System.Xml;
    using System.Collections.Generic;
    using compound_doc.handle_formula;

    // 直接遍历 CDataMgr.m_bookFmla如果结果不相同，那么写入日志
    public class CompCell : suit_base.SuiteBase
    {
        private string m_sSamplePath;
        private string m_sEtPath;
        private int m_nTraverseLevel;
        private autoIt.CAutoIt m_wndKiller;
        
        public override string GetCommand()
        {
            return ET_Improve.Global.CMD_COMPCELL;
        }

        public override void Call()
        {
            try
            {
                LoadConfig();
                etOp.EtOp.ReRegisterET(m_sEtPath);
                m_wndKiller = new autoIt.CAutoIt();
                m_wndKiller.Start();
#if CC_DUMP_FILE
                tool.Output.GetInstance().BeginDumpFile(null);
#endif
                Dictionary<string, List<tool.CAddInfoHlp.CFileInfo>> fileDic =
                    tool.CAddInfoHlp.GetFolderFiles(m_sSamplePath, ET_Improve.Global.CMD_COMPCELL);
                foreach (string sDir in fileDic.Keys)
                {
                    if (fileDic[sDir].Count == 0)
                    {
                        continue;
                    }

                    tool.LogTool.GetInstance().BeginUseLog(sDir, ET_Improve.Global.s_curCmdName);
                    foreach (tool.CAddInfoHlp.CFileInfo fi in fileDic[sDir])
                    {
#if CC_DUMP_FILE
                        tool.Output.GetInstance().DumpLine(string.Format(">>>>>>>>>>Begin File:{0}", fi.FileName));
#endif
                        CompFile(fi);
                    }
                    tool.LogTool.GetInstance().EndUseLog();
                }
                tool.Output.DumpWriteLine(">>>>>>compcell completed!<<<<<<");
            }
            catch (tool.ETImpException ex)
            {
                ex.Display();
            }
            catch (System.Exception ex)
            {
                tool.Output.DumpWriteLine(ex.ToString());
            }
            finally
            {
#if CC_DUMP_FILE
                tool.Output.GetInstance().EndDumpFile();
#endif
                m_wndKiller.Stop();
            }
        }

        protected void LoadConfig()
        {
            const string LOG_ROOT = "CompResult";
            const string LOG_SAMPLE = "SamplePath";
            const string LOG_ET_PATH = "EtPath";
            const string LOG_LEVEL = "TravelLevel";
            try
            {
                foreach (XmlNode nodeX in tool.ConfigReader.GetElementList(LOG_ROOT))
                {
                    if (nodeX.Name == LOG_SAMPLE)
                    {
                        m_sSamplePath = nodeX.InnerText;
                    }
                    else if (nodeX.Name == LOG_ET_PATH)
                    {
                        m_sEtPath = nodeX.InnerText;
                    }
                    else if (nodeX.Name == LOG_LEVEL)
                    {
                        m_nTraverseLevel = int.Parse(nodeX.InnerText);
                    }
                }
            }
            catch (System.Exception ex)
            {
                tool.Output.DumpWriteLine(ex.ToString());
                throw ex;
            }
        }

        protected void ReadFromFile(string sFileName, ref CellMgr cxMgr, bool bOldET)
        {
            try
            {
                CDataMgr datMgr = new CDataMgr(sFileName);
                datMgr.InitDataFilter(m_nTraverseLevel);
                compound_doc.CReadCompFile read = new compound_doc.CReadCompFile();
                read.ReadFile(sFileName, datMgr);
                datMgr.GetFilterData(ref cxMgr, bOldET);
#if CC_DUMP_FILE
                //OutputCell(cellList);
#endif
                read = null;
                datMgr = null;
            }
            catch (tool.ETImpException ex)
            {
                throw ex;
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.BINARY_FAILED, sFileName, null, ex);
            }
        }

        public void CompFile(tool.CAddInfoHlp.CFileInfo fi)
        {
            string sTmpFile = null;
            etOp.EtOp et = null;
            tool.Output.DumpWriteLine("compFile -->" + fi.FileName);
            try
            {
                // 1、读样张二进制文件
                CellMgr cellMgr = null;
                ReadFromFile(fi.FileName, ref cellMgr, true);
                // 2、新ET打开强制重算然后另存
                et = new etOp.EtOp();
                m_wndKiller.BeginMonitor(true);
                et.Init(m_wndKiller, true);
                et.Open(fi.FileName, fi.Password);
                sTmpFile = ETCalcuSaveAs(et, fi.FileName);
                et.Close();
                m_wndKiller.EndMonitor();
                et = null;
                System.Threading.Thread.Sleep(100);

                // 3、再分析此二进制文件
                ReadFromFile(fi.FileName, ref cellMgr, false);
                // 4、比较结果值是否相同
                cellMgr.DiffCellValue(fi.FileName);
            }
            catch (tool.ETImpException ex)
            {
                ex.AddLog();
            }
            catch (System.Exception ex)
            {
                tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.EXCEPTION, fi.FileName, null, ex);
            }
            finally
            {
                m_wndKiller.EndMonitor();
                if (et != null)
                {
                    et.Close();
                }
                tool.CTmpFileHlp.DeleteTmpFile(sTmpFile);
            }
        }

        protected string ETCalcuSaveAs(etOp.EtOp pEt, string sFileName)
        {
            try
            {
                pEt.Calculate();
                // 另存为然后返回临时文件名
                string sTmpFile = tool.CTmpFileHlp.GetSystemTmpName(sFileName);
                pEt.SaveAsFile(sTmpFile);
                return sTmpFile;
            }
            catch (tool.ETImpException ex)
            {
                throw ex;
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.EXCEPTION, sFileName, "cmpCell.ETCalcuSaveAs出错", ex);
            }
        }
    }

    // 数据类
    public class CDataMgr
    {
        private CFmlaDat m_fmlaDat;
        private CNameDat m_nameDat;
        private List<bool> m_externSheet;    // 按顺序存储，是否是跨book的ExternSheet
        private CDataFilter m_datFilter;
        private static List<string> m_sheetName;
        private string m_sFileName;

        public CDataMgr(string _FileName)
        {
            m_sFileName = _FileName;
            m_externSheet = new List<bool>();
            m_sheetName = new List<string>();
            m_fmlaDat = new CFmlaDat();
            m_nameDat = new CNameDat();
        }

        public void InitDataFilter(int _level)
        {
            m_datFilter = new CDataFilter(_level);
        }

        public void AddNameRec(byte[] dat)
        {
            CName name = new CName();
            name.Prase(dat);
            m_nameDat.AddName(name);
        }

        public void AddExternSheetRec(byte[] dat)
        {
            CExternSheet es = new CExternSheet();
            es.Parse(dat);
            m_externSheet = es.GetItems();
        }

        public void AddExternNameRec(byte[] dat)
        {
            CExternName name = new CExternName();
            name.Prase(dat);
            m_nameDat.AddExtName(name);
        }

        public void AddBoundSheetRec(byte[] dat)
        {
            biff8_BOUNDSHEET bs = new biff8_BOUNDSHEET(dat);
            m_sheetName.Add(bs.GetSheetName());
        }

        public static string GetCellString(int iSheet, int nRow, int nCol)
        {
            EtEnvironment.RangeConverter rgConv = new EtEnvironment.RangeConverter(m_sheetName.ToArray());
            EtEnvironment.RANGE rg;
            rg.nBeginSheet = iSheet;
            rg.nEndSheet = iSheet;
            rg.nBeginRow = nRow;
            rg.nEndRow = nRow;
            rg.nBeginCol = nCol;
            rg.nEndCol = nCol;

            return rgConv.Range2CellStr(rg, true, true).ToString();
        }

        public void BeginSheet()
        {
            m_fmlaDat.AddSheet();
        }

        public void EndSheet()
        {

        }

        public void AddFormulaRec(byte[] dat)
        {
            try
            {
                int nSheetIdx = m_fmlaDat.GetSheetCount() - 1;
                CFormula fmla = new CFormula();
                fmla.Prase(dat, nSheetIdx);
                // fmType代表指向的公式是什么类型
                switch (fmla.fmType)
                {
                    case FMLA_TYPE.FT_NAME:
                        m_nameDat.DoNameVolatile(fmla);
                        break;
                    case FMLA_TYPE.FT_EXTERN_NAME:
                        fmla.m_bExpVolatile = true;
                        break;
                    // 如果公式指向的是shrfmla的第一个元素，那肯定不知道，在AddShrFmla处理
                    case FMLA_TYPE.FT_SHARE:
                        fmla.m_nFmlaIdx = m_fmlaDat.UpdateShareFmla(nSheetIdx, fmla.m_firstCell);
                        break;
                    case FMLA_TYPE.FT_ARRAY:
                        fmla.m_nFmlaIdx = m_fmlaDat.UpdateArrayFmla(nSheetIdx, fmla.m_firstCell);
                        break;
                }
                m_fmlaDat.AddFormula(nSheetIdx, fmla);
                m_datFilter.UpdateFilter(fmla.m_bExpVolatile, fmla.m_bImpVolatile, fmla.m_cell);
            }
            catch (tool.ETImpException ex)
            {
                if (ex.FileName == null)
                {
                    ex.FileName = m_sFileName;
                }
                throw ex;
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, m_sFileName, "CDataMgr.AddFormulaRec:" + ex.Message);
            }
        }

        public void AddShrFmlaRec(byte[] dat)
        {
            int nSheetIdx = m_fmlaDat.GetSheetCount() - 1;
            CFormula fm = m_fmlaDat.PraseShrFmlaRec(nSheetIdx, dat);
            m_datFilter.UpdateFilter(fm.m_bExpVolatile, fm.m_bImpVolatile, fm.m_cell);
        }

        public void AddArrFmlaRec(byte[] dat)
        {
            int nSheetIdx = m_fmlaDat.GetSheetCount() - 1;
            CFormula fm = m_fmlaDat.PraseArrFmlaRec(nSheetIdx, dat);
            m_datFilter.UpdateFilter(fm.m_bExpVolatile, fm.m_bImpVolatile, fm.m_cell);
        }

        public void GetFilterData(ref CellMgr cxMgr, bool bOldET)
        {
#if CC_DUMP_FILE
            DisplayData();
#endif
            m_datFilter.StartFilter(m_fmlaDat, m_nameDat);
            m_datFilter.GetFilterData(ref cxMgr, bOldET);
        }

#if CC_DUMP_FILE
        protected void DisplayData()
        {
            try
            {
                tool.Output.GetInstance().DumpLine("------------------");
                for (int k = 0; k < m_nameDat.GetNameCount(); ++k)
                {
                    CName nm = m_nameDat.GetNameByIndex(k);
                    tool.Output.GetInstance().Dump(string.Format("name{0}:", k));
                    nm.m_fmla.DumpOutput();
                }

                for (int idx = 0; idx < m_fmlaDat.GetSheetCount(); ++idx)
                {
                    tool.Output.GetInstance().DumpLine(String.Format("********{0}********",
                        m_sheetName[idx]));
                    CSheetFmlaDat sheetFmla = m_fmlaDat.GetSheetFmla(idx);
                    int i;
                    for (i = 0; i < sheetFmla.GetFormulaCount(); ++i)
                    {
                        CFormula fm = sheetFmla.GetFormulaByIndex(i);
                        tool.Output.GetInstance().Dump("Fmla:");
                        fm.DumpOutput();
                    }
                }
                tool.Output.GetInstance().DumpLine("------------------");
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("Failed of DisplayData" + ex.Message);
            }
        }
#endif
    }

    // 筛选数据
    public class CDataFilter
    {
        private List<CellHdr> m_randCategory;      // 读文件的时候就记录下
        private List<CellHdr> m_offsetCategory;    // offset、evaluate、sumif、indirect、cell还有op_range、op_uion
        private CFmlaDat m_fmlaDat;
        private CNameDat m_nameDat;
        private int m_nTraverseLevel;
        public CDataFilter(int _level)
        {
            m_nTraverseLevel = _level;
            m_randCategory = new List<CellHdr>();
            m_offsetCategory = new List<CellHdr>();
        }

        public void UpdateFilter(bool bExp, bool bImp, CellHdr cell)
        {
            if (bExp)
            {
                m_randCategory.Add(cell);
            }
            if (bImp)
            {
                m_offsetCategory.Add(cell);
            }
        }

        public void StartFilter(CFmlaDat fmlaDat, CNameDat nameDat)
        {
            m_fmlaDat = fmlaDat;
            m_nameDat = nameDat;

            
            if (m_randCategory.Count == 0)
            {
                return;
            }

            List<CellHdr> delCell = new List<CellHdr>();
            foreach (CellHdr c in m_randCategory)
            {
                CellHdrHlp.AddListSorted(delCell, c);
            }
            foreach (CellHdr c in m_offsetCategory)
            {
                CellHdrHlp.AddListSorted(delCell, c);
            }

            int i = 0;
            for (; i < m_nTraverseLevel; ++i)
            {
                int k = StartExclude(delCell);
                if (k == 0)
                {
                    break;
                }
            }
        }

        // 返回新排除的单元格个数,shrFmla\arrFmla要多考虑一层
        protected int StartExclude(List<CellHdr> del_List)
        {
            int nDelCells = 0;
            for (int iSheet = 0; iSheet < m_fmlaDat.GetSheetCount(); ++iSheet)
            {
                CSheetFmlaDat sheetFmlaDat = m_fmlaDat.GetSheetFmla(iSheet);
                for (int i = 0; i < sheetFmlaDat.GetFormulaCount(); ++i)
                {
                    if (sheetFmlaDat.GetFormulaByIndex(i) == null)
                    {
                        continue;
                    }

                    CFormula fm = sheetFmlaDat.GetFormulaByIndex(i);
                    bool bExclude = false;
                    switch (fm.fmType)
                    {
                        case FMLA_TYPE.FT_NAME:
                            bExclude = DeNameExclude(del_List, fm.m_bExternName, fm.m_nameIndex);
                            break;
                        case FMLA_TYPE.FT_EXTERN_NAME:
                            bExclude = true;
                            break;
                        case FMLA_TYPE.FT_SHARE:
                            bExclude = DeShrFmlaExclude(del_List, fm.m_nFmlaIdx, iSheet, fm.m_cell);
                            break;
                        case FMLA_TYPE.FT_ARRAY:
                            bExclude = DeArrFmlaExclude(del_List, fm.m_nFmlaIdx, iSheet);
                            break;
                        case FMLA_TYPE.FT_NORMAL:
                            bExclude = DeNormalFmlaExclude(del_List, fm);
                            break;
                    }

                    if (bExclude)
                    {
                        CellHdrHlp.AddListSorted(del_List, fm.m_cell);
                        ++nDelCells;
                        sheetFmlaDat.ClearFormulaByIndex(i);
                    }
                }
            }

            return nDelCells;
        }

        public void GetFilterData(ref CellMgr cellMgr, bool bOldET)
        {
            if(bOldET)
            {
                cellMgr = new CellMgr(m_fmlaDat.GetSheetCount());
            }
            for (int iSheet = 0; iSheet < m_fmlaDat.GetSheetCount(); ++iSheet)
            {
                CSheetFmlaDat sheetFmlaDat = m_fmlaDat.GetSheetFmla(iSheet);
                for (int i = 0; (sheetFmlaDat != null) && i < sheetFmlaDat.GetFormulaCount(); ++i)
                {
                    CFormula fm = sheetFmlaDat.GetFormulaByIndex(i);
                    if (fm == null)
                    {
                        continue;
                    }
                    cellMgr.SetItem(bOldET, iSheet, fm.m_cell.row, fm.m_cell.col, fm.sResult);
                }
            }
        }

        protected bool DeNameExclude(List<CellHdr> del_List, bool bExternName, List<int> nameIdx)
        {
            if(bExternName)
            {
                return true;
            }

            bool bVol = false;
            for (int i = 0; i < nameIdx.Count; ++i)
            {
                bVol = IsExcludeByNmae(del_List, nameIdx[i]);
                if(bVol)
                {
                    break;
                }
            }
            return bVol;
        }

        protected bool IsExcludeByNmae(List<CellHdr> del_List, int idx)
        {
            CName name = m_nameDat.GetNameByIndex(idx);
            if (name.m_fmla.m_bExpVolatile ||
                name.m_fmla.m_bImpVolatile)
            {
                return true;
            }

            if (IsExcludeCells(del_List, name.m_fmla.m_cellRef, null) ||
                IsExcludeAreas(del_List, name.m_fmla.m_areaRef, null))
            {
                return true;
            }

            return false;
        }

        protected bool DeShrFmlaExclude(List<CellHdr> del_List, int index,
            int sheetIdx, CellHdr curCell)
        {
            CShrFmla sf = m_fmlaDat.GetShrFmla(sheetIdx, index);
            if (sf.m_fmla.m_bExpVolatile || sf.m_fmla.m_bImpVolatile)
            {
                return true;
            }
            if (IsExcludeAreas(del_List, sf.m_fmla.m_areaRef, curCell) ||
                IsExcludeCells(del_List, sf.m_fmla.m_cellRef, curCell))
            {
                return true;
            }
            return false;
        }

        protected bool DeArrFmlaExclude(List<CellHdr> del_List, int index, int sheetIdx)
        {
            CArrayFmla af = m_fmlaDat.GetArrFmla(sheetIdx, index);
            if (af.m_fmla.m_bExpVolatile || af.m_fmla.m_bImpVolatile)
            {
                return true;
            }
            if (IsExcludeAreas(del_List, af.m_fmla.m_areaRef, null) ||
                IsExcludeCells(del_List, af.m_fmla.m_cellRef, null))
            {
                return true;
            }
            return false;
        }

        protected bool DeNormalFmlaExclude(List<CellHdr> del_List, CFormula fm)
        {
            // 自己已经被排除了
            int index = del_List.BinarySearch(fm.m_cell, new ICellHdrCmp());
            if (index >= 0)
            {
                return true;
            }

            if (IsExcludeAreas(del_List, fm.m_areaRef, null) ||
                IsExcludeCells(del_List, fm.m_cellRef, null))
            {
                return true;
            }
            return false;
        }

        protected bool IsExcludeAreas(List<CellHdr> delLst,
            HashSet<AreaRef> areaRefs, CellHdr curCell)
        {
            if (areaRefs == null)
            {
                return false;
            }
            foreach (AreaRef ar in areaRefs)
            {
                if (IsExcludeArea(delLst, ar, curCell))
                {
                    return true;
                }
            }

            return false;
        }

        protected CellHdr GetTargetCell(CellRef cRef, CellHdr cur)
        {
            CellHdr cell = new CellHdr();
            cell.ixfe = cRef.idx;
            cell.row = cRef.cell.row;
            cell.col = cRef.cell.grbitCol.col;
            if (cRef.cell.grbitCol.IsRowRelative() &&
                null != cur)
            {
                cell.row += cur.row;
            }
            if (cRef.cell.grbitCol.IsColRelative() &&
                null != cur)
            {
                cell.col += cur.col;
            }

            return cell;
        }

        protected CellHdr GetTargetCell(AreaRef area, ushort offR,
            ushort offC, CellHdr cur)
        {
            CellHdr cell = new CellHdr();
            cell.row = (ushort)(area.area.rwFirst + offR);
            cell.col = (ushort)(area.area.grbitColFirst.col + offC);
            if (area.area.grbitColFirst.IsRowRelative() &&
                null != cur)
            {
                cell.row += cur.row;
            }
            if (area.area.grbitColFirst.IsColRelative() &&
                null != cur)
            {
                cell.col += cur.col;
            }
            return cell;
        }

        protected bool IsExcludeArea(List<CellHdr> delLst,
            AreaRef area, CellHdr curCell)
        {
            if (area == null)
            {
                return false;
            }

            CellHdr begCell = GetTargetCell(area, 0, 0, curCell);
            CellHdr endCell = GetTargetCell(area, (ushort)(area.area.rwTo - area.area.rwFirst),
                (ushort)(area.area.grbitColTo.col - area.area.grbitColFirst.col), curCell);
            for (ushort iR = begCell.row; iR <= endCell.row; ++iR)
            {
                for (ushort iC = begCell.col; iC <= endCell.col; ++iC)
                {
                    CellHdr item = new CellHdr(begCell.ixfe, iR, iC);
                    int index = delLst.BinarySearch(item, new ICellHdrCmp());
                    if (index >= 0)
                    {
                        return true;
                    }
                    if (delLst.Count == (~index))
                    {
                        return false;
                    }
                }
            }
            return false;
        }

        protected bool IsExcludeCells(List<CellHdr> delLst,
            HashSet<CellRef> cellRefs, CellHdr curCell)
        {
            if (cellRefs == null)
            {
                return false;
            }
            foreach (CellRef cr in cellRefs)
            {
                CellHdr ch = GetTargetCell(cr, curCell);
                int index = delLst.BinarySearch(ch, new ICellHdrCmp());
                if (index >= 0)
                {
                    return true;
                }
                if ((~index) == delLst.Count)
                {
                    return false;
                }
            }

            return false;
        }
    }

    public class CFmlaDat
    {
        private List<CSheetFmlaDat> m_fmla;
        public CFmlaDat()
        {
            m_fmla = new List<CSheetFmlaDat>();
        }

        public int GetSheetCount()
        {
            return m_fmla.Count;
        }

        public void AddSheet()
        {
            m_fmla.Add(new CSheetFmlaDat(GetSheetCount()));
        }

        public void AddFormula(int nSheetIdx, CFormula fmla)
        {
            if (nSheetIdx >= GetSheetCount())
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null,
                        "sheetIdx error in AddFormula.");
            }
            m_fmla[nSheetIdx].AddFormula(fmla);
        }

        public int UpdateShareFmla(int nSheetIdx, CellHdr firstCell)
        {
            if (nSheetIdx >= GetSheetCount() || nSheetIdx != firstCell.ixfe)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null,
                    "sheetIdx error in UpdateShareFmla.");
            }
            return m_fmla[nSheetIdx].UpdateShrFmlaInfo(firstCell);
        }

        public int UpdateArrayFmla(int nSheetIdx, CellHdr firstCell)
        {
            if (nSheetIdx >= GetSheetCount() || nSheetIdx != firstCell.ixfe)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null,
                    "sheetIdx error in UpdateArrayFmla.");
            }

            return m_fmla[nSheetIdx].UpdateArrFmlaInfo(firstCell);
        }

        public CFormula PraseShrFmlaRec(int nSheetIdx, byte[] dat)
        {
            // 读共享公式第一个元素时已经new了一个ShrFmla，因此只需要得到最后一个
            CShrFmla sf = m_fmla[nSheetIdx].GetLastShrFmla();
            sf.Prase(dat, nSheetIdx);
            // 更新ShrFmla第一个元素
            CFormula fm = m_fmla[nSheetIdx].GetLastFormula();
            fm.m_bExpVolatile = sf.m_fmla.m_bExpVolatile;
            fm.m_bImpVolatile = sf.m_fmla.m_bImpVolatile;
            return fm;
        }

        public CFormula PraseArrFmlaRec(int nSheetIdx, byte[] dat)
        {
            // 读共享公式第一个元素时已经new了一个ShrFmla，因此只需要得到最后一个
            CArrayFmla af = m_fmla[nSheetIdx].GetLastArrFmla();
            af.Prase(dat, nSheetIdx);
            // 更新ShrFmla第一个元素
            CFormula fm = m_fmla[nSheetIdx].GetLastFormula();
            fm.m_bExpVolatile = af.m_fmla.m_bExpVolatile;
            fm.m_bImpVolatile = af.m_fmla.m_bImpVolatile;
            return fm;
        }

        public CSheetFmlaDat GetSheetFmla(int nIdx)
        {
            if (nIdx < m_fmla.Count)
            {
                return m_fmla[nIdx];
            }
            else
            {
                return null;
            }
        }

        public CShrFmla GetShrFmla(int nSheetIdx, int nShrFmlaIdx)
        {
            if (nSheetIdx < m_fmla.Count)
            {
                return m_fmla[nSheetIdx].GetShrFmlaByIndex(nShrFmlaIdx);
            }
            return null;
        }

        public CArrayFmla GetArrFmla(int nSheetIdx, int nArrFmlaIdx)
        {
            if (nSheetIdx < m_fmla.Count)
            {
                return m_fmla[nSheetIdx].GetArrFmlaByIndex(nArrFmlaIdx);
            }
            return null;
        }
    }

    public class CSheetFmlaDat
    {
        private int m_nSheetIdx;
        private List<CFormula> m_cellFmla;  // 每个单元格的公式
        private List<CShrFmla> m_shareFmla;
        private List<CArrayFmla> m_arrayFmla;

        public CSheetFmlaDat(int nIdx)
        {
            m_nSheetIdx = nIdx;
            m_cellFmla = new List<CFormula>();
            m_shareFmla = new List<CShrFmla>();
            m_arrayFmla = new List<CArrayFmla>();
        }

        public void AddFormula(CFormula fm)
        {
            m_cellFmla.Add(fm);
        }

        public int GetFormulaCount()
        {
            return m_cellFmla.Count;
        }

        public CFormula GetLastFormula()
        {
            if (m_cellFmla.Count > 0)
            {
                return m_cellFmla[m_cellFmla.Count - 1];
            }
            return null;
        }

        public CFormula GetFormulaByIndex(int idx)
        {
            if (idx < m_cellFmla.Count)
            {
                return m_cellFmla[idx];
            }
            return null;
        }

        public void ClearFormulaByIndex(int idx)
        {
            if (idx < m_cellFmla.Count)
            {
                m_cellFmla[idx] = null;
            }
        }

        public CShrFmla GetLastShrFmla()
        {
            if (m_shareFmla.Count > 0)
            {
                return m_shareFmla[m_shareFmla.Count - 1];
            }
            return null;
        }

        public CArrayFmla GetLastArrFmla()
        {
            if (m_arrayFmla.Count > 0)
            {
                return m_arrayFmla[m_arrayFmla.Count - 1];
            }
            return null;
        }

        public CShrFmla GetShrFmlaByIndex(int idx)
        {
            if (idx < m_shareFmla.Count)
            {
                return m_shareFmla[idx];
            }
            return null;
        }

        public CArrayFmla GetArrFmlaByIndex(int idx)
        {
            if (idx < m_arrayFmla.Count)
            {
                return m_arrayFmla[idx];
            }
            return null;
        }

        public int UpdateShrFmlaInfo(CellHdr firstCell)
        {
            int nIndex = -1;
            CShrFmla sf = null;
            int i = 0;
            int nCount = m_shareFmla.Count;
            for (; i < nCount; ++i)
            {
                sf = m_shareFmla[i];
                if (sf.m_firstCell == firstCell)
                {
                    break;
                }
            }

            // 已知数据集中没有找到，则是shrfmla的第一个元素
            if (i == nCount)
            {
                nIndex = nCount;
                sf = new CShrFmla();
                sf.m_firstCell = firstCell;
                m_shareFmla.Add(sf);
            }
            else
            {
                nIndex = i;
            }

            return nIndex;
        }

        //遍历array firstcell，如果不存在，新建，否则就标注索引位置 
        public int UpdateArrFmlaInfo(CellHdr firstCell)
        {
            int nIndex = -1;
            CArrayFmla af = null;
            int nCount = m_arrayFmla.Count;
            int i = 0;
            for (; i < nCount; ++i)
            {
                af = m_arrayFmla[i];
                if (af.m_firstCell == firstCell)
                {
                    break;
                }
            }

            if (i == nCount)
            {
                nIndex = nCount;
                af = new CArrayFmla();
                af.m_firstCell = firstCell;
                m_arrayFmla.Add(af);
            }
            else
            {
                nIndex = i;
            }

            return nIndex;
        }
    }

    public class CNameDat
    {
        private List<CName> m_name;
        private List<CExternName> m_extName;
        public CNameDat()
        {
            m_name = new List<CName>();
            m_extName = new List<CExternName>();
        }

        public void AddName(CName _name)
        {
            m_name.Add(_name);
        }

        public void AddExtName(CExternName _name)
        {
            m_extName.Add(_name);
        }

        public CName GetNameByIndex(int idx)
        {
            if (idx < m_name.Count)
            {
                return m_name[idx];
            }
            return null;
        }

        public CExternName GetExtNameByIndex(int idx)
        {
            if (idx < m_extName.Count)
            {
                return m_extName[idx];
            }
            return null;
        }

        public int GetNameCount()
        {
            return m_name.Count;
        }

        public int GetExtNameCount()
        {
            return m_extName.Count;
        }

        public bool IsExtNameRandBetween(int idx)
        {
            if (idx < 0 || idx > m_extName.Count)
            {
                string sMsg = string.Format("IsExtNameRandBetween:out of bound. externName:idx={0}; Count={1}", idx, m_extName.Count);
                tool.Output.DumpWriteLine(sMsg);
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null, sMsg);
            }
            return m_extName[idx].bRandBetween;
        }

        public void DoNameVolatile(CFormula fmla)
        {
            foreach (int idx in fmla.m_nameIndex)
            {
                if (idx > m_name.Count)
                {
                    string sMsg = string.Format("out of bound. nameIdx:idx={0}; Count={1}", idx, m_name.Count);
                    tool.Output.DumpWriteLine(sMsg);
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null, sMsg);
                }
                DoVolatileName(idx, ref fmla.m_bExpVolatile, ref fmla.m_bImpVolatile);
            }
        }

        protected bool IsExternName(List<bool> extSheet, int itxi)
        {
            if (itxi < 0)
            {
                return false;
            }
            if (itxi > extSheet.Count)
            {
                string sMsg = string.Format("out of bound. externSheet:idx={0}; Count={1}", itxi, extSheet.Count);
                tool.Output.DumpWriteLine(sMsg);
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, null, sMsg);
            }

            return extSheet[itxi];
        }

        protected void DoVolatileName(int idx, ref bool bExp, ref bool bImp)
        {
            if (!bExp)
            {
                bExp = m_name[idx].m_fmla.m_bExpVolatile;
            }
            if (!bImp)
            {
                bImp = m_name[idx].m_fmla.m_bImpVolatile;
            }
        }
    }
    
    public class CellMgr
    {
        private List<CSheetDat> m_sheetDat;

        public CellMgr(int nSheetCount)
        {
            m_sheetDat = new List<CSheetDat>();
            for (int i = 0; i < nSheetCount; ++i)
            {
                m_sheetDat.Add(new CSheetDat(i));
            }
        }

        public void SetItem(bool bOldEt, int nSheet, ushort row, ushort col, string sRes)
        {
            if (nSheet >= m_sheetDat.Count)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.BINARY_FAILED, null, string.Format(
                    "Failed of SetItem. bOldET:{0}, sheet index:{1}, count:{2}", bOldEt, nSheet, m_sheetDat.Count));
            }
            CSheetDat sd = m_sheetDat[nSheet];
            sd.SetItem(bOldEt, row, col, sRes);
        }

        public void DiffCellValue(string sFileName)
        {
            for(int iSheet = 0; iSheet < m_sheetDat.Count; ++iSheet)
            {
                m_sheetDat[iSheet].DiffCellValue(sFileName);
            }
        }

        class CSheetDat
        {
            private int m_sheetIdx;
            private List<Cell> m_cell;
            public CSheetDat(int nSheet)
            {
                m_sheetIdx = nSheet;
                m_cell = new List<Cell>();
            }

            public void SetItem(bool bOldET, ushort row, ushort col, string sResult)
            {
                Cell cx = new Cell(row, col);
                int pos = m_cell.BinarySearch(cx, new ICellCmp());
                if (pos < 0)
                {
                    pos = ~pos;
                    AddItem(pos, cx);
                }
                if (bOldET)
                {
                    m_cell[pos].sResOld = sResult;
                }
                else
                {
                    m_cell[pos].sResNew = sResult;
                }
            }

            protected void AddItem(int idx, Cell cx)
            {
                if (idx == m_cell.Count)
                {
                    m_cell.Add(cx);
                }
                else
                {
                    m_cell.Insert(idx, cx);
                }
            }

            public void DiffCellValue(string sFileName)
            {
                for(int i = 0; i < m_cell.Count; ++i)
                {
                    Cell cx = m_cell[i];
                    if(cx.sResNew == cx.sResOld)
                    {
                        continue;
                    }
                    tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.RESULT_ERROR, sFileName,
                        string.Format("{0}: RESULT origin:{1} ETSaveAs:{2}", CDataMgr.GetCellString(
                        m_sheetIdx, cx.row, cx.col), cx.sResOld, cx.sResNew), null);                    
                }
            }
        }
    }

    class Cell
    {
        public const string NOT_SET = "NOT_SET";
        public ushort row;
        public ushort col;
        public string sResOld;
        public string sResNew;
        public Cell(ushort _row, ushort _col)
        {
            row = _row;
            col = _col;
            sResOld = NOT_SET;
            sResNew = NOT_SET;
        }
        public bool Equals(Cell other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return row.Equals(other.row) && col.Equals(other.col);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            if (obj.GetType() != typeof(Cell))
            {
                return false;
            }

            return Equals((Cell)obj);
        }

        public override int GetHashCode()
        {
            return row.GetHashCode() ^ col.GetHashCode();
        }

        public static int Compare(Cell left, Cell right)
        {
            int nRet = left.row.CompareTo(right.row);
            if (0 != nRet)
            {
                return nRet;
            }

            return left.col.CompareTo(right.col);
        }
    }

    class ICellCmp : IComparer<Cell>
    {
        public int Compare(Cell left, Cell right)
        {
            return Cell.Compare(left, right);
        }
    }
}

#endif // #if ENABLE_ET_COM